package com.face.zf.aspect;

import com.face.zf.annotation.RequestTimeMonitor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.logging.Logger;

@Aspect
@Component
public class RequestTimeAspect {

    private static final Logger log = Logger.getLogger(RequestTimeAspect.class.getName());

    @Value("${time.out.value}")
    private long timeValue;

    @Pointcut("@annotation(com.face.zf.annotation.RequestTimeMonitor)")
    public void monitoredRequest() {
    }

    @Around("monitoredRequest()")
    public Object monitorRequestTime(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RequestTimeMonitor monitorAnnotation = method.getAnnotation(RequestTimeMonitor.class);

        if (monitorAnnotation == null) {
            monitorAnnotation = method.getDeclaringClass().getAnnotation(RequestTimeMonitor.class);
        }

        long timeout = monitorAnnotation != null ? monitorAnnotation.timeout() : timeValue;

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String requestURI = request.getRequestURI();
        long startTime = System.currentTimeMillis();

        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Object> future = executor.submit(() -> {
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                throw new ExecutionException(e);
            }
        });

        try {
            // 设置超时
            Object result = future.get(timeout, TimeUnit.MILLISECONDS);
            long elapsedTime = System.currentTimeMillis() - startTime;
            logRequestTime(requestURI, method.getName(), elapsedTime);
            return result;
        } catch (TimeoutException e) {
            future.cancel(true);
            return buildTimeoutResponse();
        } catch (ExecutionException e) {
            throw e.getCause();
        } finally {
            executor.shutdownNow();
        }
    }

    private void logRequestTime(String uri, String methodName, long elapsedTime) {
        log.warning("接口:" + uri + "下的" + methodName + " 请求耗时: " + (elapsedTime / 1000.0) + " s");
    }

    private ResponseEntity<Map<String, Object>> buildTimeoutResponse() {
        log.info("接口超时");
        Map<String, Object> response = new HashMap<>();
        response.put("code", HttpStatus.REQUEST_TIMEOUT.value());
        response.put("similarity", 0);
        response.put("message", "人脸识别超过10s，接口超时");
        return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body(response);
    }
}
